在調整之前,我們先來看一下狀況!
var a = [];
// Object > Array > a(實體)
如果今天狀況是一個陣列十字的宣告的話,那麼原型鏈的關係就是陣列實體,再往上是陣列原型,再往上是物件原型。
這個透過前面幾篇文章的介紹應該沒甚麼問題。也因此這個陣列的實體可以使用陣列原型以及物件原型原型鏈上的所有方法。
function Dog (name, color, size) {
this.name = name;
this.color = color;
this.size = size;
}
var Bibi = new Dog('比比', '棕色', '小');
console.log(Bibi);
// Object > Dog > Bibi(實體)
那麼前幾個章節我們也透過 Dog
的建構函式建立了比比這隻狗(實體),他是繼承於狗,在上一層是物件的原型,同時比比可以調用 建構函式 Dog 以及 物件原型的所有方法。
好那麼我們現在的狀況回到造物主的身分,所有的狗其實都是屬於動物的一環沒錯吧!
所以我們現在需要再物件原型以及建構函式 Dog 中間新增一層叫做 Animal 的關係的話,我們應該怎麼做呢?
Object > Animal > Dog > Bibi(實體)
如果變成這樣的話,我們就可以創造出其他的物種,例如 貓
Object > Animal > Cat
首先要先來介紹一個方法,叫做 Object.create()
這個方法的主要功用呢,就是把其他的物件作為原型使用喔!
直接來看可能比較有感覺,先從簡單的例子的開始吧~
var Bibi = {
name: '比比',
color: '棕色',
size: '小',
bark: function () {
console.log(this.name + '吠叫');
}
};
// Object.create()
var Pupu = Object.create(Bibi);
console.log(Pupu);
我們讓 Pupu 繼承 Bibi為原型,雖然 Pupu 現在是空物件,但是可以透過 Pupu.name
的方法來取得並且確認的確是繼承了 Bibi 為原型
當然我們也可以直接賦予屬性給 Pupu
var Pupu = Object.create(Bibi);
Pupu.name = '噗噗';
console.log(Pupu);
這就是 Object.create()
的用法。
而且在不改變屬性的情況下,所有的值都可以以 Bibi 為預設值,並且調用 Bibi 的方法。
function Animal (family) {
this.kingdom = '動物界';
this.family = family || '人科';
}
首先我們先創立動物的建構函式,並且允許可以傳入這個動物的科別,如果都沒有傳入的話就預設是人科。
並且我們也可以在 Animal 的原型上加上動物的方法,也就是移動的方式。
Animal.prototype.move = function () {
console.log(this.name + ' 移動');
};
接下來呢我們把狗加回來,並且重點來了,我們要利用 Object.create()
的方式把狗的原型重新賦值給動物的原型
function Dog (name, color, size) {
this.name = name;
this.color = color || '白色';
this.size = size || '小';
}
Dog.prototype = Object.create(Animal.prototype);
透過這樣的方式,就可以產生我們一開始所說的階級,接下來再把狗會叫的行為給加回來
Dog.prototype.bark = function () {
console.log(this.name + '吠叫');
}
那因此呢,我們就可以再用建構式的方式,把 Bibi 這隻狗給產生出來。
function Animal (family) {
this.kingdom = '動物界';
this.family = family || '人科';
}
Animal.prototype.move = function () {
console.log(this.name + ' 移動');
};
function Dog (name, color, size) {
this.name = name;
this.color = color || '白色';
this.size = size || '小';
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.bark = function () {
console.log(this.name + '吠叫');
}
var Bibi = new Dog('比比', '棕色', '小');
console.log(Bibi);
可以看到 比比 的確繼承了狗,並且上一層是 Animal 沒錯,那麼接下來我們再來看看他是不是能夠正常的吠叫以及移動。
Bibi.bark();
Bibi.move();
好~看起來沒甚麼問題嗎~都跟我們預期的結果差不多
主要就是其實 Bibi 並沒有繼承到 Animal 定義的科別以及動物界的屬性喔!!
也因此直接輸入 Bibi.family 的話會是 undefined 喔~
為什麼會這樣呢,主要是因為其實 Dog 只有繼承 Animal 的原型,並沒有繼承整個建構函式,所以我們得在狗的建構函式內補一些內容
function Dog (name, color, size) {
// 新增了這裡
Animal.call(this, '犬科');
// 新增了這裡
this.name = name;
this.color = color || '白色';
this.size = size || '小';
}
我們透過 call 的方式,將 Dog 裡面的 this 指定給 Animal 裡面的 this,並且直接執行,就等於是幫 Dog 新增了 Animal 裡面的屬性喔!
並且也特別傳入犬科的參數,讓 this.family 的值變為犬科。
好~到這裡其實程式碼的運作已經沒甚麼問題,但可以加上最後一行讓整個程式馬的邏輯更為完善
Dog.prototype = Object.create(Animal.prototype);
// 邏輯更為完善
Dog.prototype.constructor = Dog;
// 邏輯更為完善
因為呢我們利用 Object.create
的方式繼承了 Animal,那麼這個 constructor 也會被取代掉,所以透過這樣的方式補回來。
那麼這個 constructor 又是甚麼呢?
var newAnimal = new Animal('新物種');
console.log(newAnimal);
當我們使用建構式的方式來產生一個新的物種的時候,其中 proto 的屬性 就會包含 constructor 這個屬性,那麼這個 constructor 的屬性就會指向原本的建構函式。
所以可以看到這裡 newAmimal 是由 Animal 作為建構函式所產生的物件實體,所以 newAmimal 的 constructor 就會指向 Animal。
又所以,Dog.prototype.constructor = Dog;
才可以透過 Bibi.constructor
找到狗的原型喔!!!
所以整個的程式碼會如下所示:
function Animal (family) {
this.kingdom = '動物界';
this.family = family || '人科';
}
Animal.prototype.move = function () {
console.log(this.name + ' 移動');
};
function Dog (name, color, size) {
// 新增了這裡
Animal.call(this, '犬科');
// 新增了這裡
this.name = name;
this.color = color || '白色';
this.size = size || '小';
}
Dog.prototype = Object.create(Animal.prototype);
// 邏輯更為完善
Dog.prototype.constructor = Dog;
// 邏輯更為完善
Dog.prototype.bark = function () {
console.log(this.name + '吠叫');
}
var Bibi = new Dog('比比', '棕色', '小');
console.log(Bibi);
Bibi.bark();
Bibi.move();
那麼接著我們就練習貓科的新增吧!
而貓不會吠叫只會喵喵叫,所以在貓的原型上要掛載的是喵喵叫的方法!
function Animal (family) {
this.kingdom = '動物界';
this.family = family || '人科';
}
Animal.prototype.move = function () {
console.log(this.name + ' 移動');
};
function Dog (name, color, size) {
// 新增了這裡
Animal.call(this, '犬科');
// 新增了這裡
this.name = name;
this.color = color || '白色';
this.size = size || '小';
}
Dog.prototype = Object.create(Animal.prototype);
// 邏輯更為完善
Dog.prototype.constructor = Dog;
// 邏輯更為完善
Dog.prototype.bark = function () {
console.log(this.name + '吠叫');
}
var Bibi = new Dog('比比', '棕色', '小');
console.log(Bibi);
Bibi.bark();
Bibi.move();
// var newAnimal = new Animal('新物種');
// console.log(newAnimal);
function Cat (name, color, size) {
Animal.call(this, '貓科');
this.name = name;
this.color = color || '白色';
this.size = size || '小';
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.meow = function () {
console.log(this.name + '喵喵叫');
}
var Kity = new Cat('凱蒂');
Kity.meow();
Kity.move();
Kity.bark();
那麼依照這樣的方式呢,凱蒂能夠喵喵叫,也能夠移動,但是沒辦法像狗一樣吠叫。
好~那麼這篇文章就先介紹到這邊,各位可以想想 建構函式、建構出來的實體、建構函式的原型以及建構出來的實體的 proto 屬性之間的關係喔,這個部分也會在下一篇文章更深入的彙整介紹。
這篇文章就先到這裡嚕!希望對各位有幫助~汪汪